package cc.rengu.oltp.service.base;

import cc.rengu.jradp.flowcontrol.FlowControl;
import cc.rengu.oltp.service.common.constant.AppConfigConstant;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.ServiceNameConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.*;
import cc.rengu.oltp.service.common.dao.impl.*;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.common.enums.OltpRpcdEnum;
import cc.rengu.oltp.service.common.enums.TransStatusEnum;
import cc.rengu.oltp.service.model.*;
import cc.rengu.oltp.service.realize.*;
import cc.rengu.oltp.service.realize.impl.*;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.cache.UnifiedCache;
import com.alibaba.fastjson.JSON;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 业务处理基类
 * Created by 王成 on 2018/4/16.
 */
public abstract class BizBaseService<T> extends RadpService {
    protected TxnInfo txnInfo = null;
    public TransStatusEnum transStatusEnum = TransStatusEnum.SYSTEM_REJECT;

    @Override
    protected int bizInit(String s) {
        return 0;
    }

    @Override
    protected int bizProcess(String s, int i) {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        RiskMonitorInfo riskMonitorInfo = null;
        String txnRegisterFlag = AppParamConstant.NO;
        long txnBeginTime = DateUtil.getCurrentTimeStamp();
        boolean registerTransLogFlag = false;
        String exceptionMsg = null;
        String sysSeqNum = null;
        try {
            /* Step1：业务(交易)权限检查 */
            rglog.info("---------------> 业务(交易)权限检查 <---------------");
            xmlTreeUtil.setXmlTreeLongValue(TreeNodeConstant.TXN_COST_BEGIN_TIME, txnBeginTime);
            txnInfo = transPermissionCheck();
            schedule.resetTimeout(txnInfo.getSrcTxnAuthCfg().getTxnTimeout() * 1000);
            /* Step2：生成系统流水号 */
            rglog.info("---------------> 生成系统流水号 <---------------");
            sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
            if (StringUtil.isEmptyOrNull(sysSeqNum)) {
                sysSeqNum = generateTransSeqNum();
            }
            /* Step3：业务处理前实现接口 */
            rglog.info("---------------> 业务处理前处理 <---------------");
            T request = beforeBizProcess();
            if (StringUtil.containsXssInjection(request)) {
                rglog.error("接口请求内容存在XSS攻击特殊字符!");
                throw new BizException(OltpRpcdEnum.XSS_ATTACK_REQUEST_DATA);
            }
            /* Step4：业务流量控制 */
            transFlowControl();
            /* Step5：交易流水登记 */
            rglog.info("---------------> 交易流水登记 <---------------");
            txnRegisterFlag = registerTransLog(txnInfo, sysSeqNum);
            registerTransLogFlag = true;
            /* Step6：业务逻辑处理 */
            rglog.info("---------------> 业务逻辑处理 <---------------");
            riskMonitorInfo = getRiskMonitorInfo();
            if (null != riskMonitorInfo && null != riskMonitorInfo.getTransMonitor()) {
                RiskMonitorService riskMonitorService = riskMonitorInfo.getTransMonitor();
                if (AppParamConstant.FINANCE_TXN_HEAD.equals(txnRegisterFlag) && riskMonitorService.isRiskTrans()) {
                    rglog.error("当前交易经风控检测为风险交易，拒绝交易!");
                    throw new BizException(OltpRpcdEnum.RISK_TRANS);
                }
            }
            BizResponse response = callBizService(request);
            /* Step7：应答码转换 */
            rglog.info("---------------> 应答码转换 <---------------");
            transStatusEnum = transferRespCode(response.getRspSysId(), response.getRespCode(), response.getRespDesc());
            response.setTransStatus(transStatusEnum);
            /* Step8：业务处理后实现接口 */
            rglog.info("---------------> 业务处理后处理 <---------------");
            afterBizProcess(response);
            /* Step9：超时业务处理 */
            if (0 >= schedule.getTimeout()) {
                rglog.info("---------------> 超时业务处理 <---------------");
                rglog.info("服务调用超时,重新转换错误码的映射关系:<{}-{}> ——> <{}-{}>", response.getRespCode(), response.getRespDesc(), OltpRpcdEnum.BIZ_PROCESS_TIMEOUT.getRespCode(), OltpRpcdEnum.BIZ_PROCESS_TIMEOUT.getRespDesc());
                transStatusEnum = transferRespCode(response.getRspSysId(), OltpRpcdEnum.BIZ_PROCESS_TIMEOUT.getRespCode(), OltpRpcdEnum.BIZ_PROCESS_TIMEOUT.getRespDesc());
                handleTimeout(txnInfo);
            }
        } catch (Exception e) {
            String respCode;
            String respDesc;
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            exceptionMsg = byteArrayOutputStream.toString();
            rglog.error("业务异常信息:<{}>", exceptionMsg);
            if (e instanceof BizException) {
                respCode = ((BizException) e).getCode();
                respDesc = e.getMessage();
                rglog.error("业务处理失败，业务返回码:<{}>，业务返回信息:<{}>", respCode, respDesc);
                if (null != txnInfo && null != respCode && respCode.trim().equals(OltpRpcdEnum.BIZ_PROCESS_TIMEOUT.getRespCode())) {
                    /* 业务处理超时，按照交易信息配置表中的超时处理规则处理 */
                    handleTimeout(txnInfo);
                }
            } else {
                rglog.error("系统处理失败，返回信息:<{}>", e);
                respCode = OltpRpcdEnum.OLTP_PROCESS_ERROR.getRespCode();
                respDesc = StringUtil.isEmptyOrNull(e.getMessage()) ? OltpRpcdEnum.OLTP_PROCESS_ERROR.getRespDesc() : e.getMessage();
            }
            transStatusEnum = transferRespCode(AppParamConstant.SYS_ID, respCode, respDesc);
        } finally {
            /* Step10：打印交易耗时日志 */
            logTransProcessInfo(transStatusEnum, txnInfo, txnBeginTime);
            /* Step11：更新交易流水 */
            rglog.info("---------------> 更新交易流水信息 <---------------");
            if (!registerTransLogFlag) {
                try {
                    registerTransLog(txnInfo, sysSeqNum);
                } catch (Exception e) {
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
                    exceptionMsg = byteArrayOutputStream.toString();
                    rglog.error("登记交易流水失败:{}", exceptionMsg);
                }
            } else {
                updateTransLog(transStatusEnum, txnInfo, txnRegisterFlag);
            }
            /* Step12：风险监控及交易熔断控制检查 */
//            riskMonitorAndTransFusingCtrl(riskMonitorInfo, transStatusEnum, exceptionMsg);
        }
        return 0;
    }

    @Override
    protected int bizTerminate(String s) {
        return 0;
    }

    /**
     * 业务调用前处理
     *
     * @return 业务处理请求结构
     * @throws Exception 异常
     */
    protected abstract T beforeBizProcess() throws Exception;

    /**
     * 业务处理服务调用
     *
     * @param request 业务处理信息
     * @return 业务处理应答结果
     * @throws Exception 异常
     */
    protected abstract BizResponse callBizService(T request) throws Exception;

    /**
     * 业务调用后处理
     *
     * @param response 业务响应结果
     * @throws Exception 异常
     */
    protected abstract void afterBizProcess(BizResponse response) throws Exception;

    /**
     * 获取监控报文信息实体
     *
     * @return 包含一个监控对象实例以及需要监控的数据，如果不需要监控，可返回null
     */
    protected abstract RiskMonitorInfo getRiskMonitorInfo();

    /**
     * 获取流量控制类型
     *
     * @return 流量控制类型集合（key：流量控制类型，value：流量控制描述）
     */
    public Map<String, String> getFlowControlType() {
        return null;
    }


    /**
     * 应答码转换客户化处理默认实现
     * 该方法可在交易实现类中覆写， 继承层级可能大于1因此需要public，且由于原有为private因此从未被复写过，不用考虑兼容问题
     * 覆写时可使用成员变量transStatusEnum判断原有是否已经应答码转换成功
     * 使用情况可参考如下：
     * 1. 不判断transStatusEnum是否为null
     * 1.0 通过客户化逻辑修改应答码及描述
     * 1.1 直接返回null
     * 注：以上适用于内部错误码相同但是请求方规定不同交易有特殊的应答码
     * <p>
     * 2. 判断transStatusEnum是否为null
     * 2.0 通过客户化逻辑修改应答码及描述
     * 2.1 transStatusEnum为null时通过客户化逻辑返回不同的状态，如借记返回失败，贷记返回成功
     * 2.2 transStatusEnum不为null时直接返回或者返回null
     * 注：以上适用于内部错误码相同但是请求方规定不同交易有特殊的应答码同时若应答码转换配置不存在希望根据借贷记等逻辑返回不同的交易状态
     *
     * @return 返回null则不改变基类中得到的交易状态，否则将交易状态修改为客户化逻辑返回的交易状态
     */
    public TransStatusEnum transferRspCodeCustom(String rspSysId, String rspCode, String rspMsg) {
        return null;
    }

    /**
     * 交易权限权限
     *
     * @return 交易配置信息
     * @throws Exception 业务处理异常
     */
    private TxnInfo transPermissionCheck() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        BizHeader bizHeader = new BizHeader();
        ConvertUtil.convertOutput(bizHeader);
        if (bizHeader.getInstId().isEmpty() || bizHeader.getSrcSysId().isEmpty() || bizHeader.getTxnNum().isEmpty() || bizHeader.getBizType().isEmpty()){
            rglog.error("instId:<{}> or channelId:<{}> or txnNum:<{}> or bizType:<{}> is null.",bizHeader.getInstId(),bizHeader.getSrcSysId(),bizHeader.getTxnNum(),bizHeader.getBizType());
            throw new BizException(OltpRpcdEnum.PARAM_IS_EMPTY_OR_NULL);
        }
        String bizType = (null == bizHeader.getBizType() || bizHeader.getBizType().isEmpty())?"00":bizHeader.getBizType();
        String channelId =  (null == bizHeader.getChanlId() || bizHeader.getChanlId().isEmpty())?"*":bizHeader.getChanlId();
        String txnKey = bizHeader.getInstId().trim() + bizHeader.getSrcSysId().trim() + channelId.trim() + bizHeader.getTxnNum().trim() + bizType.trim();
        TxnInfo txnInfo = (TxnInfo) xmlTreeUtil.getXmlTreeObjectValue(TreeNodeConstant.TXN_INFO,TxnInfo.class);
        if (null == txnInfo || !txnInfo.getTxnKey().equals(txnKey)){
            TxnInfoService txnInfoService = new TxnInfoServiceImpl();
            txnInfo = txnInfoService.getCurrentTxnInfo();
        }

        /* 交易权限检查控制 */
        ChannelService channelService = new ChannelServiceImpl();
        if (!channelService.channnelCheck(txnInfo)) {
            rglog.error("instId:<{}>,channel:<{}>,txnNum:<{}>,bizType:<{}> is not support.",
                    txnInfo.getSrcTxnAuthCfg().getInstId(), txnInfo.getSrcTxnAuthCfg().getMsgSrcId(), txnInfo.getSrcTxnAuthCfg().getTxnNum(), txnInfo.getSrcTxnAuthCfg().getBizType());
            throw new BizException(OltpRpcdEnum.TRANS_NOT_SUPPORT);
        }
        rglog.debug("交易权限配置信息:{}",JSON.toJSONString(txnInfo.getSrcTxnAuthCfg()));
        return txnInfo;
    }

    /**
     * 业务流量控制
     */
    private void transFlowControl() {
        FlowControl flowControl = new FlowControl();
        Map<String, String> flowContorlTypeMap = getFlowControlType();
        if (null != flowContorlTypeMap && !flowContorlTypeMap.isEmpty()) {
            rglog.info("---------------> 业务流量控制 <---------------");
            for (String flowControlType : flowContorlTypeMap.keySet()) {
                String flowControlDesc = flowContorlTypeMap.get(flowControlType);
                int iReturnCode = flowControl.isOverRun(flowControlType);
                if (0 != iReturnCode) {
                    rglog.error("【{}-{}】业务流量控制超限！", flowControlType, flowControlDesc);
                    throw new BizException(OltpRpcdEnum.TRANS_FLOW_CTRL_PROCESS_ERROR);
                }
            }
        }
    }

    /**
     * 生成交易流水号
     *
     * @return 系统流水号
     * @throws Exception 业务异常
     */
    private String generateTransSeqNum() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        String msgSrcId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.MSG_SRC_ID);
        SequenceMapper sequenceMapper = new SequenceMapperImpl();
        String transSeqNo = sequenceMapper.getNextTransSequenceNo(8);
        if (null == transSeqNo) {
            rglog.error("生成交易流水序列号失败!");
            throw new BizException(OltpRpcdEnum.GENERATE_SEQUENCE_ERROR);
        }
        /* 基于流水号生成规则，格式化固定长度的法人行标识 */
        instId = StringUtil.isEmptyOrNull(instId)?AppParamConstant.DEFAULT_INSTID:instId;
        String txnInstId;
        if (instId.length() < 4){
            String format = "%0" + (4 - instId.length()) + "d";
            txnInstId = instId + String.format(format,0);
        }else {
            txnInstId = instId.substring(0,4);
        }
        /* 基于流水号生成规则，格式化固定长度的源系统标识 */
        if (msgSrcId.length() < 4) {
            String format = "%0" + (4 - msgSrcId.length()) + "d";
            msgSrcId = msgSrcId + String.format(format,0);
        }else {
            msgSrcId = msgSrcId.substring(0,4);
        }
        /* 流水号规则：4位渠道标识 + 4位法人行标识 + 8位日期 + 6位时间 + 2位服务器集群标识 + 8位序号 */
        String sysSeqNum = msgSrcId + txnInstId + DateUtil.getCurrentDate() + DateUtil.getCurrentTime() + AppParamConstant.CLUSERT_ID + transSeqNo;
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM,sysSeqNum);
        rglog.debug("instId:<{}>,sysSeqNum:<{}>",instId,sysSeqNum);
        return sysSeqNum;
    }

    /**
     * 交易流水登记
     */
    private String registerTransLog(TxnInfo txnInfo, String sysSeqNum) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String txnRegisterFlag = AppParamConstant.NO;
        int iReturnCode;
        /* Step1：设置流水登记标志 */
        if (null == txnInfo) {
            String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
            String msgSrcId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.MSG_SRC_ID);
            String txnNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TXN_NUM);
            String bizType = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE);
            rglog.error("不支持的交易:instId<{}>,msgSrcId<{}>,txnNum<{}>,bizType<{}>", instId, msgSrcId, txnNum, bizType);
            throw new BizException(OltpRpcdEnum.TRANS_NOT_SUPPORT);
        } else if (null != txnInfo.getSrcTxnAuthCfg().getTxnRegisterFlag() && !txnInfo.getSrcTxnAuthCfg().getTxnRegisterFlag().trim().isEmpty()) {
            txnRegisterFlag = txnInfo.getSrcTxnAuthCfg().getTxnRegisterFlag();
        } else if (txnInfo.getSrcTxnAuthCfg().getTxnNum().startsWith(AppParamConstant.FINANCE_TXN_HEAD)) {
            txnRegisterFlag = AppParamConstant.FINANCE_TXN_HEAD;
        } else if (txnInfo.getSrcTxnAuthCfg().getTxnNum().startsWith(AppParamConstant.NON_FINANCE_TXN_HEAD)) {
            txnRegisterFlag = AppParamConstant.NON_FINANCE_TXN_HEAD;
        } else if (txnInfo.getSrcTxnAuthCfg().getTxnNum().startsWith(AppParamConstant.MANAGE_TXN_HEAD)) {
            txnRegisterFlag = AppParamConstant.MANAGE_TXN_HEAD;
        }
        /* Step1.1：获取或生成全局流水号 */
        String globalTraceNumber = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.GLOBAL_TRACE_NUMBER);
        String globalPathNumber = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.GLOBAL_PATH_NUMBER);
        String globalTraceServiceImpl = XmlConfigUtil.getXmlConfigCacheKeyValue(AppConfigConstant.APPLICATION_CONF_NODE, AppConfigConstant.GLOBAL_TRACE_SERVICE_IMPL);
        if (!StringUtil.isEmptyOrNull(globalTraceServiceImpl)) {
            Class clasz = Class.forName(globalTraceServiceImpl.trim());
            Object methodInstance = clasz.newInstance();
            if (StringUtil.isEmptyOrNull(globalTraceNumber)) {
                Method generateGlobalTraceNumberMethod = clasz.getMethod(GlobalTraceService.GENETATE_GLOBAL_TRACE_NUMBER);
                globalTraceNumber = (String) generateGlobalTraceNumberMethod.invoke(methodInstance);
                globalTraceNumber = StringUtil.isEmptyOrNull(globalTraceNumber) ? txnInfo.getSrcTxnAuthCfg().getInstId() + sysSeqNum : globalTraceNumber;
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.GLOBAL_TRACE_NUMBER, globalTraceNumber);
            }
            if (StringUtil.isEmptyOrNull(globalPathNumber)) {
                Method generateGlobalTracePathMethod = clasz.getMethod(GlobalTraceService.GENETATE_GLOBAL_PATH_NUMBER);
                globalPathNumber = (String) generateGlobalTracePathMethod.invoke(methodInstance);
                globalPathNumber = StringUtil.isEmptyOrNull(globalPathNumber) ? "1" : globalPathNumber;
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.GLOBAL_PATH_NUMBER, globalPathNumber);
            }
        }
        /* Step1.2：获取客户端请求IP */
        String clientIp = xmlTreeUtil.getXmlTreeStringValue("_MESSAGEHEAD/http/RemoteIp");
        rglog.info("客户端<{}>发起<{}-{}>的交易请求!", clientIp, txnInfo.getSrcTxnAuthCfg().getTxnNum(), txnInfo.getSrcTxnAuthCfg().getTxnName());
        /* Step2：登记交易流水 */
        switch (txnRegisterFlag) {
            case AppParamConstant.FINANCE_TXN_HEAD:
                /* Step2.1：登记金融类交易流水 */
                FinanceTxn financeTxn = new FinanceTxn();
                ConvertUtil.convertOutput(financeTxn);
                initFinanceTxnInfo(financeTxn, txnInfo);
                financeTxn.setGlobalTraceNumber(globalTraceNumber);
                financeTxn.setGlobalPathNumber(globalPathNumber);
                financeTxn.setChannelIp(clientIp);
                ConvertUtil.convertInput(financeTxn);
                iReturnCode = transLogProcess(ExpandTxnService.REGISTER, AppParamConstant.FINANCE_TXN_HEAD, financeTxn);
                break;
            case AppParamConstant.NON_FINANCE_TXN_HEAD:
                /* Step2.1：非金融类交易 */
                NonFinanceTxn nonFinanceTxn = new NonFinanceTxn();
                ConvertUtil.convertOutput(nonFinanceTxn);
                initNonFinanceTxnInfo(nonFinanceTxn, txnInfo);
                nonFinanceTxn.setGlobalTraceNumber(globalTraceNumber);
                nonFinanceTxn.setGlobalPathNumber(globalPathNumber);
                nonFinanceTxn.setChannelIp(clientIp);
                ConvertUtil.convertInput(nonFinanceTxn);
                iReturnCode = transLogProcess(ExpandTxnService.REGISTER, AppParamConstant.NON_FINANCE_TXN_HEAD, nonFinanceTxn);
                break;
            case AppParamConstant.MANAGE_TXN_HEAD:
                /* Step2.1：管理类交易 */
                ManageTxn manageTxn = new ManageTxn();
                ConvertUtil.convertOutput(manageTxn);
                initManageTxnInfo(manageTxn, txnInfo);
                manageTxn.setGlobalTraceNumber(globalTraceNumber);
                manageTxn.setGlobalPathNumber(globalPathNumber);
                manageTxn.setChannelIp(clientIp);
                ConvertUtil.convertInput(manageTxn);
                iReturnCode = transLogProcess(ExpandTxnService.REGISTER, AppParamConstant.MANAGE_TXN_HEAD, manageTxn);
                break;
            default:
                rglog.info("交易<{}-{}>不登记交易流水！", txnInfo.getTxnKey(), txnInfo.getSrcTxnAuthCfg().getTxnName());
                return txnRegisterFlag;
        }
        if (0 != iReturnCode) {
            rglog.error("登记交易流水失败,iReturnCode:<{}>", iReturnCode);
            throw new BizException(OltpRpcdEnum.REGISTER_TRANS_LOG_ERROR);
        }
        return txnRegisterFlag;
    }

    /**
     * 更新交易流水状态
     *
     * @param transStatusEnum 交易状态枚举
     */
    private void updateTransLog(TransStatusEnum transStatusEnum, TxnInfo txnInfo, String txnRegisterFlag) {
        int iReturnCode;
        try {
            switch (txnRegisterFlag) {
                case AppParamConstant.FINANCE_TXN_HEAD:
                    /* 金融类交易 */
                    FinanceTxn financeTxn = new FinanceTxn();
                    ConvertUtil.convertOutput(financeTxn);
                    financeTxn.setTxnStatus(transStatusEnum.getStatus());
                    financeTxn.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
                    iReturnCode = transLogProcess(ExpandTxnService.UPDATE, AppParamConstant.FINANCE_TXN_HEAD, financeTxn);
                    break;
                case AppParamConstant.NON_FINANCE_TXN_HEAD:
                    /* 非金融类交易 */
                    NonFinanceTxn nonFinanceTxn = new NonFinanceTxn();
                    ConvertUtil.convertOutput(nonFinanceTxn);
                    nonFinanceTxn.setTxnStatus(transStatusEnum.getStatus());
                    nonFinanceTxn.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
                    iReturnCode = transLogProcess(ExpandTxnService.UPDATE, AppParamConstant.NON_FINANCE_TXN_HEAD, nonFinanceTxn);
                    break;
                case AppParamConstant.MANAGE_TXN_HEAD:
                    /* 管理类交易 */
                    ManageTxn manageTxn = new ManageTxn();
                    ConvertUtil.convertOutput(manageTxn);
                    manageTxn.setTxnStatus(transStatusEnum.getStatus());
                    manageTxn.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
                    iReturnCode = transLogProcess(ExpandTxnService.UPDATE, AppParamConstant.MANAGE_TXN_HEAD, manageTxn);
                    break;
                default:
                    rglog.info("交易<{}-{}>未登记交易流水，无需更新！", txnInfo.getTxnKey(), txnInfo.getSrcTxnAuthCfg().getTxnName());
                    return;
            }
            if (0 != iReturnCode) {
                rglog.error("更新交易流水失败,iReturnCode:<{}>", iReturnCode);
                throw new BizException(OltpRpcdEnum.UPDATE_TRANS_LOG_ERROR);
            }
        } catch (Exception e) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("更新交易流水表状态失败,transStatus:<{}>,异常信息:<{}>", transStatusEnum.getStatus(), byteArrayOutputStream.toString());
        }
    }

    /**
     * 登记交易日志信息
     *
     * @param oprType   操作类型：REISTER-登记，UPDATE-更新
     * @param txnType   流水类型：P-金融类流水，T-非金融类流水，M-管理类流水
     * @param txnObject 流水信息
     * @return 0-成功，其它-失败
     * @throws Exception 异常
     */
    private int transLogProcess(String oprType, String txnType, Object txnObject) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* Step1:获取需要登记的扩展交易流水信息 */
        ExpandTxnInfo expandTxnInfo = null;
        String expandTxnServiceImpl = XmlConfigUtil.getXmlConfigCacheKeyValue(AppConfigConstant.APPLICATION_CONF_NODE, AppConfigConstant.EXPAND_TXN_SERVICE_IMPL);
        if (!StringUtil.isEmptyOrNull(expandTxnServiceImpl)) {
            Class clasz = Class.forName(expandTxnServiceImpl.trim());
            Object methodInstance = clasz.newInstance();
            Method expandTxnProcessMethod = ExpandTxnService.REGISTER.equals(oprType) ? clasz.getMethod(ExpandTxnService.REGISTER) : clasz.getMethod(ExpandTxnService.UPDATE);
            expandTxnInfo = (ExpandTxnInfo) expandTxnProcessMethod.invoke(methodInstance);
        }
        /* Step2:开启数据库事务 */
        String dbPoolName = XmlConfigUtil.getXmlConfigCacheKeyValue(AppParamConstant.DATABASE_CONF_NODE, AppParamConstant.OLTP_POOL_NAME);
        String corporation = xmlTreeUtil.getXmlTreeStringValue(AppParamConstant.CORPORATION);
        if (null != corporation && !corporation.isEmpty()) {
            String dbPoolCashNode = corporation.trim() + AppParamConstant.DATABASE_CONF_NODE;
            dbPoolName = XmlConfigUtil.getXmlConfigCacheKeyValue(dbPoolCashNode, AppParamConstant.OLTP_POOL_NAME);
        }
        DbsUtil dbsUtil = new DbsUtil(dbPoolName);
        dbsUtil.dbsBeginTransaction();
        /* Step3:登记交易流水及扩展信息 */
        int iReturnCode = 0;
        if (ExpandTxnService.REGISTER.equals(oprType)) {
            switch (txnType) {
                case AppParamConstant.FINANCE_TXN_HEAD:
                    /* Step3.1:登记金融类交易流水信息 */
                    FinanceTxnMapper financeTxnMapper = new FinanceTxnMapperImpl();
                    iReturnCode = financeTxnMapper.insertFinanceTxn((FinanceTxn) txnObject);
                    break;
                case AppParamConstant.NON_FINANCE_TXN_HEAD:
                    /* Step3.1:登记非金融类交易流水信息 */
                    NonFinanceTxnMapper nonFinanceTxnMapper = new NonFinanceTxnMapperImpl();
                    iReturnCode = nonFinanceTxnMapper.insertNonFinanceTxn((NonFinanceTxn) txnObject);
                    break;
                case AppParamConstant.MANAGE_TXN_HEAD:
                    /* Step3.1:登记管理类交易流水信息 */
                    ManageTxnMapper manageTxnMapper = new ManageTxnMapperImpl();
                    iReturnCode = manageTxnMapper.insertManageTxn((ManageTxn) txnObject);
                    break;
            }
        } else if (ExpandTxnService.UPDATE.equals(oprType)) {
            switch (txnType) {
                case AppParamConstant.FINANCE_TXN_HEAD:
                    FinanceTxnMapper financeTxnMapper = new FinanceTxnMapperImpl();
                    iReturnCode = financeTxnMapper.updateFinanceTxnByPrimaryKey((FinanceTxn) txnObject);
                    break;
                case AppParamConstant.NON_FINANCE_TXN_HEAD:
                    NonFinanceTxnMapper nonFinanceTxnMapper = new NonFinanceTxnMapperImpl();
                    iReturnCode = nonFinanceTxnMapper.updateNonFinanceTxnByPrimaryKey((NonFinanceTxn) txnObject);
                    break;
                case AppParamConstant.MANAGE_TXN_HEAD:
                    ManageTxnMapper manageTxnMapper = new ManageTxnMapperImpl();
                    iReturnCode = manageTxnMapper.updateManageTxnByPrimaryKey((ManageTxn) txnObject);
                    break;
            }
        } else {
            rglog.error("不支持的操作类型:<{}>", oprType);
        }
        if (0 == iReturnCode) {
            if (null != expandTxnInfo) {
                String sqlStr = (String) UnifiedCache.get(AppParamConstant.SQL_MAPPER_CACHE, expandTxnInfo.getSqlString().trim());
                iReturnCode = dbsUtil.dbsExecuteTransactionSql(sqlStr, expandTxnInfo.getExpandTxn(), null);
                if (0 != iReturnCode) {
                    rglog.error("登记/更新扩展交易流水信息失败,iReturnCode:<{}>，expandTxnInfo:<{}>", iReturnCode, JSON.toJSONString(expandTxnInfo));
                    dbsUtil.dbsEndTransaction(false);
                } else {
                    dbsUtil.dbsEndTransaction(true);
                }
            } else {
                dbsUtil.dbsEndTransaction(true);
            }
        } else {
            dbsUtil.dbsEndTransaction(false);
        }
        return iReturnCode;
    }

    /**
     * 应答码转换（转换后的响应码设置到XML内部树）
     *
     * @param rspSysId 业务响应源系统标识，当值为NULL时，为本业务系统返回
     * @param rspCode  源系统响应码
     * @param rspMsg   源系统响应码描述
     * @return 交易状态
     */
    private TransStatusEnum transferRespCode(String rspSysId, String rspCode, String rspMsg) {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        try {
            /* Step1：业务响应码转换 */
            RespCodeService respCodeService = new RespCodeServiceImpl();
            transStatusEnum = respCodeService.transferRespCode(rspSysId, rspCode, rspMsg);
            /* Step2：应答码转换客户化处理 */
            TransStatusEnum transStatusEnumTmp = transferRspCodeCustom(rspSysId, rspCode, rspMsg);
            if (null != transStatusEnumTmp) {
                transStatusEnum = transStatusEnumTmp;
            }
            if (null == transStatusEnum) {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.RESP_CODE, OltpRpcdEnum.RSP_CODE_TRANSFER_ERROR.getRespCode());
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.RESP_DESC, rspCode + ":" + rspMsg);
                transStatusEnum = TransStatusEnum.SYSTEM_REJECT;
            }
        } catch (Exception ex) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ex.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("应答码转换异常信息:<{}>", byteArrayOutputStream.toString());
        }
        return transStatusEnum;
    }

    /**
     * 超时业务处理
     */
    private void handleTimeout(TxnInfo txnInfo) {
        int iReturnCode;
        if (null != txnInfo.getSrcTxnExcpCfg()) {
            /* Step1：登记存储转发表 */
            TransSafMsgService transSafMsgService = new TransSafMsgServiceImpl();
            transSafMsgService.registerTransSafMsgInfo(txnInfo.getSrcTxnExcpCfg().getSafGapType(), txnInfo.getSrcTxnExcpCfg().getRepeatTime(), txnInfo.getSrcTxnExcpCfg().getRepeatNum());
            /* Step2：触发异常处理流程 */
            if (AppParamConstant.EXCP_HANDLE_SYNC.equals(txnInfo.getSrcTxnExcpCfg().getExcpHandleFlag().trim())) {
                /* Step2.1：超时同步处理 */
                iReturnCode = schedule.callService(txnInfo.getSrcTxnExcpCfg().getExcpServiceName().trim(), 0);
                if (0 != iReturnCode) {
                    rglog.error("同步调用超时服务失败,iReturnCode:<{}>", iReturnCode);
                }
            } else if (AppParamConstant.EXCP_HANDLE_ASYN.equals(txnInfo.getSrcTxnExcpCfg().getExcpHandleFlag().trim())) {
                /* Step2.2：超时异步处理 */
                String gapType = ("0".equals(txnInfo.getSrcTxnExcpCfg().getSafGapType().trim()) || "1".equals(txnInfo.getSrcTxnExcpCfg().getSafGapType().trim())) ? txnInfo.getSrcTxnExcpCfg().getSafGapType().trim() : "0";
                int queryNum = txnInfo.getSrcTxnExcpCfg().getRepeatNum() > 0 ? txnInfo.getSrcTxnExcpCfg().getRepeatNum() : 1;
                int gapTime = txnInfo.getSrcTxnExcpCfg().getRepeatTime() > 0 ? txnInfo.getSrcTxnExcpCfg().getRepeatTime() * 1000 : 3000;
                iReturnCode = asynnotify.transactionNotice(txnInfo.getSrcTxnExcpCfg().getExcpServiceName().trim(), queryNum, Integer.parseInt(gapType), gapTime);
                if (0 != iReturnCode) {
                    rglog.error("异步调用超时服务失败,iReturnCode:<{}>", iReturnCode);
                }
            }
        } else {
            rglog.info("交易<{}>未配置异常交易!", txnInfo.getTxnKey());
        }
    }

    /**
     * 初始化金融交易流水表信息
     *
     * @param financeTxn 金融交易流水表
     * @throws Exception 异常
     */
    private void initFinanceTxnInfo(FinanceTxn financeTxn, TxnInfo txnInfo) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* 交易日期 */
        financeTxn.setTxnDate(DateUtil.getCurrentDate());
        /* 交易时间 */
        financeTxn.setTxnTime(DateUtil.getCurrentTime());
        /* 内部交易码 */
        financeTxn.setTxnNum(txnInfo.getSrcTxnAuthCfg().getTxnNum());
        /* 交易状态 */
        financeTxn.setTxnStatus(TransStatusEnum.TRANS_INIT.getStatus());
        /* 交易传输时间 */
        if (null == financeTxn.getTransDateTime()) {
            String transDate = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_DATE);
            String transTime = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_TIME);
            financeTxn.setTransDateTime(transDate + transTime);
        }
        /* 前端流水号 */
        if (null == financeTxn.getFrontSeqNum()) {
            financeTxn.setFrontSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRACE_NO));
            if (null == financeTxn.getFrontSeqNum()){
                financeTxn.setFrontSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM));
            }
        }
        /* 源系统标识 */
        if (null == financeTxn.getMsgSrcId()) {
            financeTxn.setMsgSrcId(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SRC_SYS_ID));
        }
        /* 交易渠道标识 */
        if (null == financeTxn.getTransChannelId()) {
            financeTxn.setTransChannelId(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.CHANNEL_ID));
        }
        /* 商户订单号 */
        if (null == financeTxn.getMchntOrderId()) {
            financeTxn.setMchntOrderId(financeTxn.getFrontSeqNum());
        }
        /* 订单币种 */
        if (null == financeTxn.getOrderCcy()) {
            financeTxn.setOrderCcy(AppParamConstant.DEFAULT_ORDER_CCY);
        }
        /* 应用系统IP */
        financeTxn.setAppSysIp(InetAddress.getLocalHost().getHostAddress());
        /* 创建时间 */
        financeTxn.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
    }

    /**
     * 初始化非金融机构流水表信息
     *
     * @param nonFinanceTxn 非金融交易流水表信息
     * @throws Exception 异常
     */
    private void initNonFinanceTxnInfo(NonFinanceTxn nonFinanceTxn, TxnInfo txnInfo) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* 交易日期 */
        nonFinanceTxn.setTxnDate(DateUtil.getCurrentDate());
        /* 交易时间 */
        nonFinanceTxn.setTxnTime(DateUtil.getCurrentTime());
        /* 内部交易码 */
        nonFinanceTxn.setTxnNum(txnInfo.getSrcTxnAuthCfg().getTxnNum());
        /* 交易状态 */
        nonFinanceTxn.setTxnStatus(TransStatusEnum.TRANS_INIT.getStatus());
        /* 交易传输时间 */
        if (null == nonFinanceTxn.getTransDateTime()) {
            String transDate = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_DATE);
            String transTime = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_TIME);
            nonFinanceTxn.setTransDateTime(transDate + transTime);
        }
        /* 前端流水号 */
        if (null == nonFinanceTxn.getFrontSeqNum()) {
            nonFinanceTxn.setFrontSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRACE_NO));
            if (null == nonFinanceTxn.getFrontSeqNum()){
                nonFinanceTxn.setFrontSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM));
            }
        }
        /* 源系统标识 */
        if (null == nonFinanceTxn.getMsgSrcId()) {
            nonFinanceTxn.setMsgSrcId(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SRC_SYS_ID));
        }
        /* 交易渠道标识 */
        if (null == nonFinanceTxn.getTransChannelId()) {
            nonFinanceTxn.setTransChannelId(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.CHANNEL_ID));
        }
        /* 商户订单号 */
        if (null == nonFinanceTxn.getMchntOrderId()) {
            nonFinanceTxn.setMchntOrderId(xmlTreeUtil.getXmlTreeStringValue(nonFinanceTxn.getSysSeqNum()));
        }
        /* 应用系统IP */
        nonFinanceTxn.setAppSysIp(InetAddress.getLocalHost().getHostAddress());
        /* 创建时间 */
        nonFinanceTxn.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
    }

    /**
     * 初始化管理类交易流水表
     *
     * @param manageTxn 管理类交易流水表信息
     * @throws Exception 异常
     */
    private void initManageTxnInfo(ManageTxn manageTxn, TxnInfo txnInfo) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* 交易日期 */
        manageTxn.setTxnDate(DateUtil.getCurrentDate());
        /* 交易时间 */
        manageTxn.setTxnTime(DateUtil.getCurrentTime());
        /* 内部交易码 */
        manageTxn.setTxnNum(txnInfo.getSrcTxnAuthCfg().getTxnNum());
        /* 交易状态 */
        manageTxn.setTxnStatus(TransStatusEnum.TRANS_INIT.getStatus());
        /* 交易传输时间 */
        if (null == manageTxn.getTransDateTime()) {
            String transDate = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_DATE);
            String transTime = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_TIME);
            manageTxn.setTransDateTime(transDate + transTime);
        }
        /* 前端流水号 */
        if (null == manageTxn.getFrontSeqNum()) {
            manageTxn.setFrontSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRACE_NO));
            if (null == manageTxn.getFrontSeqNum()){
                manageTxn.setFrontSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM));
            }
        }
        /* 源系统标识 */
        if (null == manageTxn.getMsgSrcId()) {
            manageTxn.setMsgSrcId(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SRC_SYS_ID));
        }
        /* 交易渠道标识 */
        if (null == manageTxn.getTransChannelId()) {
            manageTxn.setTransChannelId(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.CHANNEL_ID));
        }
        /* 应用系统IP */
        manageTxn.setAppSysIp(InetAddress.getLocalHost().getHostAddress());
        /* 创建时间 */
        manageTxn.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
    }

    /**
     * 登记交易耗时信息
     *
     * @param txnBeginTime    交易开始时间
     * @param transStatusEnum 交易状态
     */
    private void logTransProcessInfo(TransStatusEnum transStatusEnum, TxnInfo txnInfo, long txnBeginTime) {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        try {
            long txnEndTime = DateUtil.getCurrentTimeStamp();
            if (xmlTreeUtil.isExist(TreeNodeConstant.TXN_COST_END_TIME)) {
                txnEndTime = xmlTreeUtil.getXmlTreeLongValue(TreeNodeConstant.TXN_COST_END_TIME);
            }
            long txnCost = txnEndTime - txnBeginTime;
            xmlTreeUtil.setXmlTreeLongValue(TreeNodeConstant.TXN_COST, txnCost);
            String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
            String respCode = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.RESP_CODE);
            String respDesc = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.RESP_DESC);
            String errorDesc = "<";
            errorDesc += (null != respCode && !respCode.isEmpty()) ? respCode.trim() : "";
            errorDesc = ("<".equals(errorDesc.trim()) ? "<" : errorDesc + "-");
            errorDesc += (null != respDesc && !respDesc.isEmpty()) ? respDesc.trim() : "";
            errorDesc = ("<".equals(errorDesc.trim()) || "<-".equals(errorDesc.trim())) ? "" : errorDesc + ">";
            String transResult = TransStatusEnum.SYSTEM_REJECT.getDesc() + errorDesc;
            if (null != transStatusEnum) {
                transResult = transStatusEnum.getDesc() + (TransStatusEnum.TRANS_SUCC.getStatus().equals(transStatusEnum.getStatus()) ? "" : errorDesc);
            }
            rglog.info("【{}-{}】交易处理{},总耗时: {} ms,交易流水号:【{}】", txnInfo.getSrcTxnAuthCfg().getTxnNum(), txnInfo.getSrcTxnAuthCfg().getTxnName().trim(), transResult, txnCost, sysSeqNum);
        } catch (Exception e) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("登记交易耗时异常信息:<{}>", byteArrayOutputStream.toString());
        }
    }

    /**
     * 风险监控及交易熔断控制
     *
     * @param riskMonitorInfo 风险监控组件
     * @param transStatusEnum 交易状态
     */
    private void riskMonitorAndTransFusingCtrl(RiskMonitorInfo riskMonitorInfo, TransStatusEnum transStatusEnum, String exceptionMsg) {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        try {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TRANS_STATUS, (null == transStatusEnum) ? TransStatusEnum.SYSTEM_REJECT.getStatus() : transStatusEnum.getStatus());
            /* Step1：风险监控及告警推送 */
            if (null != riskMonitorInfo && null != riskMonitorInfo.getTransMonitor()) {
                RiskMonitorService riskMonitorService = riskMonitorInfo.getTransMonitor();
                boolean monitorPushFlag = riskMonitorService.isMonitor();
                boolean alarmPushFlag = riskMonitorService.isAlarm();
                boolean riskCollectFlag = riskMonitorService.isRiskCollect();
                if (monitorPushFlag || alarmPushFlag || riskCollectFlag) {
                    /* Step1.1：将交易监控信息缓存到Redis */
                    TransMonitorService transMonitorService = new TransMonitorServiceImpl();
                    transMonitorService.publishTransMonitorInfo();
                    /* Step1.2：异步推送风险监控及告警信息 */
                    xmlTreeUtil.setXmlTreeStringValue(AppParamConstant.MONITOR_PUSH_FLAG, monitorPushFlag ? AppParamConstant.YES : AppParamConstant.NO);
                    xmlTreeUtil.setXmlTreeStringValue(AppParamConstant.ALARM_PUSH_FLAG, alarmPushFlag ? AppParamConstant.YES : AppParamConstant.NO);
                    xmlTreeUtil.setXmlTreeStringValue(AppParamConstant.RISK_COLLECT_FLAG, riskCollectFlag ? AppParamConstant.YES : AppParamConstant.NO);
                    xmlTreeUtil.setXmlTreeStringValue(AppParamConstant.EXCEPTION_MSG, exceptionMsg);
                    xmlTreeUtil.setXmlTreeObjectValue(AppParamConstant.ALARM_MSG, riskMonitorInfo.getMessage());
                    xmlTreeUtil.setXmlTreeStringValue(AppParamConstant.RISK_MONITOR_INFO, riskMonitorInfo.getTransMonitor().getClass().getName());
                    int iReturnCode = asynnotify.transactionNotice(ServiceNameConstant.MONITOR_RISK_ALARM_SERVICE, 1, 0, 0);
                    if (0 != iReturnCode) {
                        rglog.error("异步推送风险监控及告警信息失败,iReturnCode:<{}>", iReturnCode);
                    }
                }
            }
            /* Step2：交易熔断控制处理 */
            SysParamService sysParamService = new SysParamServiceImpl();
            String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
            SysParam sysParam = sysParamService.getSysParamInfo(instId, AppParamConstant.PLAT_SYS_PARAM, AppParamConstant.TRANS_FRUSING_CTRL_FLAG);
            if (null != sysParam && AppParamConstant.YES.equals(sysParam.getParamValue().trim())) {
                int iReturnCode = asynnotify.transactionNotice(ServiceNameConstant.TRANS_FUSING_SERVICE, 1, 0, 0);
                if (0 != iReturnCode) {
                    rglog.error("异步调用交易熔断控制服务失败,iReturnCode:<{}>", iReturnCode);
                }
            }
        } catch (Exception e) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("风险监控及交易熔断控制异常:<{}>", byteArrayOutputStream.toString());
        }
    }
}
